本系列文章已重新編修,並在加入部分 ES6 新篇章後集結成書,有興趣的朋友可至天瓏書局選購,感謝大家支持。
購書連結 https://www.tenlong.com.tw/products/9789864344130
讓我們再次重新認識 JavaScript!
前面花了好幾天的時間介紹變數與型別,接著我們繼續來要介紹的重點:運算式 (Expression) 與 運算子(Operator)。
JavaScript 的語法基本上可以分為兩大類,「敘述句 (Statement)」 與 「運算式 (Expression)」。
if
判斷式等等都可以被歸類於此。var foo;
function
時的參數 (arguments),或者透過 =
賦值時,在 =
「右側」的部分都屬於運算式的部分。var a = 10 * 10;
上例 =
右側的 10 * 10
就是運算式。
在運算式中,會透過提供一些數值給「運算子」(Operator) 進行運算,進而得到一個運算的結果。
例如我們從小就學過的四則運算「加、減、乘、除」都是屬於運算子的一種。
運算子的類型很多,依照性質來分類,大致上可以分成下面幾種:
由於篇幅的關係,系列文只會針對常見以及容易搞混的部分來做解說。
更詳細的內容各位可以參閱 MDN: 運算式與運算子 。
JavaScript 各種運算子當中,最常見的就屬「算術運算子」了。
簡單來說,算術運算子包括了大家所熟知的數學四則運算「加、減、乘、除」等。
一般來說,四則運算的算術運算子在多數的程式語言中,應該可以算是最單純的算術運算子,但是在 JavaScript (更準確的說法應該是 ECMAScript) 當中,運算後的結果可能會跟你所想的不太ㄧ樣。
加號 +
的使用非常簡單,如果你想要表示 1 + 2
這個算式的話,可以這樣寫:
var a = 1 + 2;
console.log(a); // 3
如果加號 +
前後雙方都是「數字」的話,確實是最單純的情況。
但我們前面在介紹變數的時候,有提到「特殊的數字」這種東西 (忘記的朋友請回頭參閱:Day 03 變數與資料型別)。
什麼是「特殊的數字」?
Infinity
、 -Infinity
,以及 NaN
都屬於「特殊的數字」。
先看 Infinity
系列:
Infinity + Infinity // Infinity
-Infinity + -Infinity // -Infinity
-Infinity + Infinity // NaN
NaN
的話,則是只要有其中一個是 NaN
,那麼結果就必定是 NaN
:
10 + NaN // NaN
Infinity + NaN // NaN
-Infinity + NaN // NaN
到目前為止還只是單純數字的狀況。 那麼,假設加號 +
兩側的其中一方,不是數字而是「字串」呢?
100 + "100" // "100100"
100 + "ABC" // "100ABC"
"ABC" + "XYZ" // "ABCXYZ"
在上面的範例當中可以看到,當加號 +
兩側的其中一方是字串的情況下,加號 +
會將兩者都視為「字串」連接在一起。
也就是說,其中一方是字串,另一端會被「自動轉型」為字串後,連接在一起。
以 number
、boolean
、 object
的情況來說,轉型時會去呼叫它們的 .toString()
的 「原型方法」[註1] 去取得對應的字串。
而 null
與 undefined
則是透過 JavaScript 的 String()
函數來將它們分別轉為 "null"
與 "undefined"
。
10 + 'abc' // "10abc"
true + 'abc' // "trueabc"
100 + {} // "100[object Object]"
// 當數字與 undefined 相加時,undefined 會被嘗試轉成數字,也就是 NaN
123 + undefined // NaN
"abc" + undefined // "abcundefined"
// 當數字與 null 相加時,null 會被嘗試轉成數字,也就是 0
123 + null // 123
"123" + null // "123null"
另外,在一個很長的運算式中,可能會有「數字」與「字串」的混搭算式,如:
var num1 = 10;
var num2 = 100;
var str = "10 加 100 的數字會是" + num1 + num2;
猜猜看,此時 str
的結果會是什麼?
答案是 「 "10 加 100 的數字會是10100" 」。
會有這樣的結果是由於運算式的計算是「由左而右」且「先乘除後加減」的模式來運算。 也就是說,前面的字串會先與 num1
相加,再把結果與 num2
做相加。
若要避免這樣的問題時,可以在 num1 + num2
的算式中,用小括號 ( )
包覆起來:
var num1 = 10;
var num2 = 100;
var str = "10 加 100 的數字會是" + (num1 + num2);
str
的結果就會是預期中的「"10 加 100 的數字會是110"」了。
再來是減號 -
。 如同前面的加法一樣,如果只是單純的數字算式:
var a = 10 - 3 ;
console.log(a); // 7
那麼結果就會是單純的數值運算。
如果是「特殊的數字」之 Infinity
系列:
Infinity - Infinity // NaN
-Infinity - -Infinity // NaN
-Infinity - Infinity // -Infinity
Infinity - -Infinity // Infinity
如果其中一方是 NaN
的話,那麼結果必定是 NaN
。
另外,與加號 +
不同的是,當其中一方不是「數字」的情況下:
string
、 boolean
、 undefined
與 null
)Number()
嘗試將數值轉為「數字」,再進行運算。100 - "50" // 50
100 - "abc" // NaN
// false 經過 Number() 轉型成數字後,會變成 0
100 - false // 100
// true 經過 Number() 轉型成數字後,會變成 1
100 - true // 99
100 - undefined // NaN
100 - null // 100
valueOf()
方法 [註2] 先求得對應的數值,如果得到 NaN
,那麼結果就是 NaN
。valueOf()
方法的話,則是透過 toString()
先轉成「字串」後,再以 Number()
嘗試將數值轉為「數字」後進行運算。// 簡單物件
100 - { } // NaN
// 自訂物件,透過 Object.prototype.valueOf 來指定物件的 value
function Obj(number) {
this.num = number;
};
Obj.prototype.valueOf = function(){
return this.num;
};
var o = new Obj(50);
// 因為 o.valueOf() 的值為 50
100 - o // 50
關於物件的 prototype
與 valueOf()
的部分,我們在往後文章會有詳細篇幅介紹,這裡只需要知道自動轉型的規則即可。
相較前面的加法、減法的規則,乘法運算子就單純許多。
乘法運算子由一個「星號」 *
來代表,用來計算前後兩個數值的乘積。
var a = 10 * 10 ;
console.log(a); // 100
在前後兩者都是數字的情況下,計算結果就是兩個數值的乘積。
如果計算結果超出 JavaSCript 的數字範圍,那麼就會看結果是正數或負數來決定是 Infinity
或是 -Infinity
。
當然如果其中一個是 NaN
的話,那麼結果必定也是 NaN
。
而依照 IEEE754 標準的規定, Infinity * 0
的結果也是 `NaN。
如果有其中一個不是數字的話,那麼 JavaScript 就會先在背後以 Nubmer()
作轉換後再進行計算,如:
100 * "10" // 1000
100 * abc // NaN
100 * true // 100
100 * false // 0
100 * {} // NaN
除法與乘法的規則類似。
除號在 JavaScript 用一個「斜線」/
來表示。
var a = 100 / 10 ;
console.log(a); // 10
在前後兩者都是數字的情況下,計算結果就是兩個數值的商。
但是,在除數為 0
的情況下:
Infinity
-Infinity
0
,則結果為 NaN
當然,如果有其中一個是 NaN
,則結果也會是 NaN
。
如果有其中一個不是數字的話,那麼 JavaScript 就會先在背後以 Nubmer()
作轉換後再進行計算。
除了基本的四則運算之外,JavaScript 也有取餘數的運算子,以「百分比符號」 %
來表示。
使用方式與除號類似,但得到的值是除法運算後的「餘數」:
var a = 100 % 33;
console.log(a); // 1
在前後兩者都是數字的情況下,計算結果就是除法運算後的「餘數」。
而被除數是 Infinity
或 -Infinity
的情況下,則取餘數後結果都會是 NaN
。
Infinity % 0 // NaN
Infinity % 100 // NaN
Infinity % Infinity // NaN
Infinity % -Infinity // NaN
被除數是一般數值,而除數為 Infinity
的情況下,則結果為被除數:
100 % Infinity // 100
0 % Infinity // 0
被除數是一般數值,而除數為 0
的情況下,則結果也是 NaN
。
當然,如果有其中一個是 NaN
,則結果也會是 NaN
。
與除法一樣的是,如果有其中一個不是數字的話,那麼 JavaScript 就會先在背後以 Nubmer()
作轉換後再進行計算。
[註1] 原型方法: JavaScript 與其他物件導向語言不一樣的地方是,它的繼承是 "prototype-base" 的。 也就是說,即便是基本型別的數值,除了 null
與 undefined
屬於特殊用途,並沒有相對應的原始型別包裹物件之外,都有它們對應的構造函數,或稱包裝器 (wrapper) 。 而這些「基本型別」的數值理論上是不會有對應的方法 (method),但可以由原始物件繼承而來。這部分在後續講到原型鍊時會有詳細說明。
[註2] valueOf()
是用來回傳特定物件相對應原始型別的值,當 JavaScript 的物件在進行運算時,都會透過 valueOf()
或 toString()
方法,取回該物件對應的原始型別的值再進行運算。
沒想到四則運算也可以寫這麼長一篇,在後續的文章當中,我們會繼續來介紹 JavaScript 的其他運算子,以上就是今天分享的內容。
您好,謝謝您的文章讓我獲益良多,不過除號部分我用Chrome dev tool試了一下,被除數(除號前面的數)跟除數(除號後面的數)似乎寫反了?還是如果我哪裡搞錯了再麻煩告知我,謝謝您
你好,真的是我寫反了,感謝提醒
大大好,請問文章中 100 + {} // "100[object Object]" 它的結果是怎麼樣子轉換得出來的? 謝謝
你好,文中有提到,當 JavaScript 的物件在進行運算時,都會透過 valueOf() 或 toString() 方法,取回該物件對應的原始型別的值再進行運算。
當 100 + {}
的時候,JavaScript 會嘗試將 {}
透過 toString()
自動轉型,這裡你可以試試:
const obj = {};
obj.toString(); // "[object Object]"
於是最後變成 "100[object Object]"
感謝!!